package com.codemonkey.lq.qd.facotry.support;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import com.codemonkey.lq.qd.ServiceContext;
import com.codemonkey.lq.qd.facotry.config.AbstractServiceDefinition;
import com.codemonkey.lq.qd.facotry.config.ActionDefinition;
import com.codemonkey.lq.qd.facotry.config.ExecuteEventMulticaster;
import com.codemonkey.lq.qd.facotry.config.ExecutionEvent;
import com.codemonkey.lq.qd.facotry.config.SConfiguration;
import com.codemonkey.lq.qd.facotry.config.SqlSessionContianer;
import com.codemonkey.lq.qd.facotry.config.event.ExecutionEventObjectFactory;
import com.codemonkey.lq.qd.facotry.config.event.SimpleExecuteEventMulticaster;
import com.codemonkey.lq.qd.facotry.config.executer.ServiceHandler;

/**
 * 本类所处的package直接仿造spring-beans中的GenericBeanDefinition
 * TODO 还是觉得应该将这份工作交给ServiceContext类来做,让ServiceDefinition尽量作为一个DOMAIN OBJECT(2017-01-14);;;这里是不是应该进行一个整个Service期间的sqlSessionFactory的管理,其中的Action执行时,不进行SqlSession的管理,而将SqlSession的打开/关闭交给Service
 * @author LQ
 */
public final class GenericServiceDefinition extends AbstractServiceDefinition implements
		SqlSessionContianer {

	private final SConfiguration configuration;
	private final ExecuteEventMulticaster executeEventMulticaster = SimpleExecuteEventMulticaster.getSingleton();
	private final Map<SqlSessionFactory, SqlSession> sqlSessionCahce = new HashMap<SqlSessionFactory, SqlSession>(
			5);

	public GenericServiceDefinition(SConfiguration configuration) {
		super();
		this.configuration = configuration;
	}

	@Override
	public void execute(ServiceContext serviceContext) throws Exception {
		innerDealServiceContext(serviceContext);

		Collection<ServiceHandler> shGroup = configuration.getServiceHandlerGroup();

		// 前置处理
		for (ServiceHandler serviceHandler : shGroup) {
			boolean tof = serviceHandler.preHandle(serviceContext, configuration);
			if (!tof) {
				break;
			}
		}

		try {
			for (ActionDefinition ad : this.getActionGroup()) {
				ad.execute(serviceContext);
			}

			//TODO 这里的多个sqlSession有问题,例如,前一个成功,后一个失败.
			for (SqlSession sqlSession : sqlSessionCahce.values()) {
				sqlSession.commit();
			}
			
		} catch (RuntimeException e) {
			e.printStackTrace();

			for (SqlSession sqlSession : sqlSessionCahce.values()) {
				sqlSession.rollback();
			}

			ExecutionEvent event =  ExecutionEventObjectFactory.getExecutionEvent(true, this, serviceContext);
			event.setError(e);
			executeEventMulticaster.multicastEvent(event);
			
			throw e;
		} finally {
			//清空
			sqlSessionCahce.clear();

			for (SqlSession sqlSession : sqlSessionCahce.values()) {
				sqlSession.close();
			}
		}

		// 后置处理
		for (ServiceHandler serviceHandler : shGroup) {
			serviceHandler.afterHandle(serviceContext, configuration);
		}

		ExecutionEvent event =  ExecutionEventObjectFactory.getExecutionEvent(true, this, serviceContext);
		executeEventMulticaster.multicastEvent(event);
	}

	/**
	 * 将从外部收集来的参数推入到执行上下文容器中(Execute Context Container)
	 * @param sc
	 */
	protected void innerDealServiceContext(ServiceContext sc) {
		Map<String, Object> outParamsContainer = sc.getOutParamsContainer();
		Map<String, Object> executeContextContainer = sc.getExecuteContextContainer();

		executeContextContainer.putAll(outParamsContainer);
	}

	@Override
	public SConfiguration getConfiguration() {
		return configuration;
	}

	@Override
	public SqlSession getSqlSession(SqlSessionFactory sqlSessionFactory) {
		SqlSession sqlSession = sqlSessionCahce.get(sqlSessionFactory);
		if (null != sqlSession) {
			return sqlSession;
		}

		SqlSession openedSession = sqlSessionFactory.openSession();
		sqlSessionCahce.put(sqlSessionFactory, openedSession);
		return openedSession;
	}

}
